package com.github.maojx0630.translate.translate.dict;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.maojx0630.translate.TranslateConfig;
import com.github.maojx0630.translate.common.Etag;
import com.github.maojx0630.translate.translate.dict.select.TranslateSelect;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * @author 毛家兴
 * @since 2024-01-31 16:32
 */
public class ValueCache {

    private final TranslateConfig config;

    private final TranslateSelect select;

    private final Cache<String, String> dictCache;

    private final Cache<String, String> tableCache;

    private final TranslateConfig.DictConfig dictConfig;

    private final Map<String, String> tabLastSql = new HashMap<>();

    private final ConcurrentMap<String, String> lastSqlCache = new ConcurrentHashMap<>();

    public ValueCache(TranslateSelect select, TranslateConfig config) {
        this.config = config;
        this.select = select;
        this.dictConfig = config.getDictConfig();
        switch (config.getCacheType()) {
            case LFU:
                dictCache = CacheUtil.newLFUCache(config.getCacheCapacity(), config.getCacheTime());
                tableCache = CacheUtil.newLFUCache(config.getCacheCapacity(), config.getCacheTime());
                break;
            case LRU:
                dictCache = CacheUtil.newLRUCache(config.getCacheCapacity(), config.getCacheTime());
                tableCache = CacheUtil.newLRUCache(config.getCacheCapacity(), config.getCacheTime());
                break;
            case NOT:
                dictCache = CacheUtil.newNoCache();
                tableCache = CacheUtil.newNoCache();
                break;
            default:
                dictCache = CacheUtil.newTimedCache(config.getCacheTime());
                tableCache = CacheUtil.newTimedCache(config.getCacheTime());
        }
        if (CollUtil.isNotEmpty(config.getTableConfig().getOtherLastSql())) {
            if (config.getTableConfig().isTableIgnoreCase()) {
                config.getTableConfig().getOtherLastSql().forEach((k, v) -> tabLastSql.put(k.toUpperCase(), v));
            } else {
                tabLastSql.putAll(config.getTableConfig().getOtherLastSql());
            }
        }
    }

    public String get(String value, TranslateField tf) {

        // 多值情况
        if (StrUtil.isNotBlank(tf.split()) && StrUtil.contains(value, tf.split())) {
            List<String> valueList = StrUtil.split(value, tf.split());
            List<String> selectList = new ArrayList<>();
            for (String s : valueList) {
                String selectValue = getValue(s, tf);
                if (selectValue != null) {
                    selectList.add(selectValue);
                }
            }
            if (selectList.isEmpty()) {
                return ifNullSwitch(value, tf);
            }
            if (selectList.size() == 1) {
                return selectList.getFirst();
            }
            if (tf.distinct()) {
                return StrUtil.join(tf.join(), selectList.stream().distinct().collect(Collectors.toList()));
            } else {
                return StrUtil.join(tf.join(), selectList);
            }
        } else {
            // 单值情况
            return getValue(value, tf);
        }
    }

    private String getValue(String value, TranslateField tf) {
        String lastSql = getLastSql(tf.tableName(), tf.lastSql());
        if (StrUtil.isNotBlank(tf.tableName())) {
            String key =
                    StrUtil.join("@", tf.tableName(), tf.columnName(), tf.value(), value, lastSql, tf.remark());
            key = Etag.tag(key);
            // 查询table
            return tableCache.get(
                    key,
                    () -> {
                        String selectValue =
                                select.selectTable(tf.tableName(), tf.columnName(), tf.value(), value, lastSql, tf.remark());
                        if (selectValue == null) {
                            return ifNullSwitch(value, tf);
                        } else {
                            return selectValue;
                        }
                    });
        } else {
            String key = StrUtil.join("@", tf.value(), value, lastSql, tf.remark());
            key = Etag.tag(key);
            // 查询dist
            return dictCache.get(
                    key,
                    () -> {
                        String selectValue = select.selectDict(tf.value(), value, lastSql, tf.remark());
                        if (selectValue == null) {
                            return ifNullSwitch(value, tf);
                        } else {
                            return selectValue;
                        }
                    });
        }
    }

    private String getLastSql(String tableName, String tableLastSql) {
        TranslateConfig.TableConfig tableConfig = config.getTableConfig();
        if (StrUtil.isNotBlank(tableName)) {
            if (tableConfig.isTableIgnoreCase()) {
                tableName = tableName.toUpperCase();
            }
        } else {
            tableName = "";
        }
        if (StrUtil.isBlank(tableLastSql)) {
            tableLastSql = "";
        }
        String cacheKey = Etag.tag(tableLastSql + "@" + tableLastSql);
        String lastSql = lastSqlCache.get(cacheKey);
        if (lastSql != null) {
            return lastSql;
        }
        if (StrUtil.isNotBlank(tableName)) {
            if (StrUtil.isNotBlank(tableLastSql)) {
                lastSql = tableLastSql;
            } else {
                String sql = tabLastSql.get(tableName);
                if (StrUtil.isBlank(sql)) {
                    sql = tableConfig.getDefaultLastSql();
                }
                if (StrUtil.isBlank(sql)) {
                    lastSql = "";
                } else {
                    lastSql = sql;
                }
            }
        } else {
            if (StrUtil.isNotBlank(tableLastSql)) {
                lastSql = tableLastSql;
            } else {
                String sql = dictConfig.getLastSql();
                if (StrUtil.isBlank(sql)) {
                    lastSql = "";
                } else {
                    lastSql = sql;
                }
            }
        }
        lastSqlCache.put(cacheKey, lastSql);
        return lastSql;
    }

    private String ifNullSwitch(String value, TranslateField tf) {
        return switch (tf.ifNull()) {
            case EMPTY:
                yield "";
            case NULL:
                yield null;
            case ORIGINAL:
                yield value;
            case OTHER_VALUE:
                yield tf.ifNullValue();
        };
    }

    public void clearDict() {
        dictCache.clear();
    }

    public void clearTable() {
        tableCache.clear();
    }
}
